/**
 * \file driver/net/rs232.c
 * \author Davide Gessa
 * \date 12-05-2009
 */
#include "rs232.h"
#include <types.h>
#include <arch.h>



driver_t driver_rs232 = 
{
	.name = "rs232\0",
	.desc = "serial network standard\0",
	.author = "Davide Gessa\0"
};



/**
 * Inizializza le porte rs232i
 */
uint32_t rs232_init()
{
	int x;
	
	rs232_init_port(COM1);
	rs232_init_port(COM2);
	rs232_init_port(COM3);
	rs232_init_port(COM4);
	
	for(x = 0; x < 4; x++)
		combuf[x].where = combuf[x].size = combuf[x].point = 0;
		
	handler_reg(IRQ_COM13, &rs232_callback);
	handler_reg(IRQ_COM24, &rs232_callback);
}
	
	
/**
 * Inizializza una porta rs232
 */
void rs232_init_port(uint32_t port)
{
	io_outb(port + 1, 0x00);    // Disable all interrupts
	io_outb(port + 3, 0x80);    // Enable DLAB (set baud rate divisor)
	io_outb(port + 0, 0x03);    // Set divisor to 3 (lo byte) 38400 baud
	io_outb(port + 1, 0x00);    //                  (hi byte)
	io_outb(port + 3, 0x03);    // 8 bits, no parity, one stop bit
	io_outb(port + 2, 0xC7);    // Enable FIFO, clear them, with 14-byte threshold
	io_outb(port + 4, 0x0B);    // IRQs enabled, RTS/DSR set
}


/**
 * Invia un dato ad una porta
 */
void rs232_send(uint32_t port, uint8_t data)
{
	while ((io_inb(port + 5) & 0x20) == 0);

	io_outb(port, data);
}


/**
 * Legge un carattere dal buffer
 */
uint8_t rs232_receive(uint32_t port)
{
	char ch = 0;
	switch(port)
	{
		case COM1:
			if(combuf[0].size > 0)
			{
				ch = combuf[0].buf[combuf[0].where];
				combuf[0].where++;
				combuf[0].size--;
			}
			return ch;
					
		case COM2:
			if(combuf[1].size > 0)
			{
				ch = combuf[1].buf[combuf[1].where];
				combuf[1].where++;
				combuf[1].size--;
			}
			return ch;
			
		case COM3:
			if(combuf[2].size > 0)
			{
				ch = combuf[2].buf[combuf[2].where];
				combuf[2].where++;
				combuf[2].size--;
			}
			return ch;
			
		case COM4:
			if(combuf[3].size > 0)
			{
				ch = combuf[3].buf[combuf[3].where];
				combuf[3].where++;
				combuf[3].size--;
			}
			return ch;
	}
}


/**
 * Handler delle interruzioni per le porte com
 */
static void rs232_callback(regs_t *reg)
{
	if((io_inb(COM1 + 5) & 1) != 0)
	{
		combuf[0].buf[combuf[0].point] = io_inb(COM1);
		combuf[0].size++;
		combuf[0].point++;
	}	
	if((io_inb(COM2 + 5) & 1) != 0)
	{
		combuf[1].buf[combuf[1].point] = io_inb(COM2);
		combuf[1].size++;
		combuf[1].point++;
	}		
	if((io_inb(COM3 + 5) & 1) != 0)
	{
		combuf[2].buf[combuf[2].point] = io_inb(COM3);
		combuf[2].size++;
		combuf[2].point++;
	}		
	if((io_inb(COM4 + 5) & 1) != 0)
	{
		combuf[3].buf[combuf[3].point] = io_inb(COM4);
		combuf[3].size++;
		combuf[3].point++;
	}		
}



/**
 * Invia un frame
 * 
 * \param frame buffer dati
 * \param size dimensione frame
 * \return byte inviati
 */
uint32_t rs232_send_frame(char *frame, uint32_t size)
{

}



/**
 * Riceve un frame
 * 
 * \param frame buffer dati
 * \param size dimensione massima del frame
 * \return dimensione effettiva frame ricevuto (0 = nessun frame in coda)
 */
uint32_t rs232_recv_frame(char *frame, uint32_t size)
{
	
	return 0;
}
